#include "CGIVulkanDevice.h"
#include "CGIVulkan.h"
#include "CGIVulkanContext.h"
#include "../CGICommandList.h"

namespace Alice {
	// Mirror GPixelFormats with format information for buffers
	VkFormat GVulkanBufferFormat[EPixelFormat::PF_NUM];

	// Mirror GPixelFormats with format information for buffers
	VkFormat GVulkanSRGBFormat[EPixelFormat::PF_NUM];
	// Minimum capabilities required for a Vulkan format to be considered as supported
	static constexpr EPixelFormatCapabilities kDefaultTextureCapabilities = EPixelFormatCapabilities::TextureSample;
	// Passthrough to specify we want to keep the initial BlockBytes value set in the PixelFormat
	static constexpr int32 kDefaultBlockBytes = -1;
	void VulkanPhysicalDeviceFeatures::Query(VkPhysicalDevice PhysicalDevice, uint32 APIVersion)
	{
		VkPhysicalDeviceFeatures2 PhysicalDeviceFeatures2={};
		PhysicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;

		PhysicalDeviceFeatures2.pNext = &Core_1_1;
		Core_1_1 = {};
		Core_1_1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;

		if (APIVersion >= VK_API_VERSION_1_2)
		{
			Core_1_1.pNext = &Core_1_2;
			Core_1_2 = {};
			Core_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
		}

		if (APIVersion >= VK_API_VERSION_1_3)
		{
			Core_1_2.pNext = &Core_1_3;
			Core_1_3 = {};
			Core_1_3.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
		}
		vkGetPhysicalDeviceFeatures2(PhysicalDevice, &PhysicalDeviceFeatures2);
		Core_1_0 = PhysicalDeviceFeatures2.features;
		Core_1_0.robustBufferAccess = VK_TRUE ;
		
		Core_1_0.shaderResourceResidency = VK_FALSE;
		Core_1_0.shaderResourceMinLod = VK_FALSE;
		Core_1_0.sparseBinding = VK_FALSE;
		Core_1_0.sparseResidencyBuffer = VK_FALSE;
		Core_1_0.sparseResidencyImage2D = VK_FALSE;
		Core_1_0.sparseResidencyImage3D = VK_FALSE;
		Core_1_0.sparseResidency2Samples = VK_FALSE;
		Core_1_0.sparseResidency4Samples = VK_FALSE;
		Core_1_0.sparseResidency8Samples = VK_FALSE;
		Core_1_0.sparseResidencyAliased = VK_FALSE;
	}
	CGIVulkanDevice::CGIVulkanDevice(VkPhysicalDevice inGPU,uint32 inAPIVersion) {
		mVkPhysicalDevice = inGPU;
		mAPIVersion=inAPIVersion;
		mVkPhysicalDeviceProperties = {};
		VkPhysicalDeviceProperties2KHR physicalDeviceProperties2 = {};
		physicalDeviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
		mVkPhysicalDeviceIDProperties = {};
		mVkPhysicalDeviceIDProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
		physicalDeviceProperties2.pNext = &mVkPhysicalDeviceIDProperties;
		mVkPhysicalDeviceSubgroupProperties = {};
		mVkPhysicalDeviceSubgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
		mVkPhysicalDeviceIDProperties.pNext = &mVkPhysicalDeviceSubgroupProperties;
		vkGetPhysicalDeviceProperties2(inGPU, &physicalDeviceProperties2);
		mVkPhysicalDeviceProperties = physicalDeviceProperties2.properties;
		mGraphicsQueue=NULL;
		mComputeQueue=NULL;
		mTransferQueue=NULL;
		mPresentQueue=NULL;
		mFenceManager=NULL;
	}
	void CGIVulkanDevice::InitGPU(){
		uint32 queueCount = 0;
		vkGetPhysicalDeviceQueueFamilyProperties(mVkPhysicalDevice, &queueCount, nullptr);
		mVkQueueFamilyProperties.resize(queueCount);
		vkGetPhysicalDeviceQueueFamilyProperties(mVkPhysicalDevice, &queueCount, mVkQueueFamilyProperties.data());
		mVulkanPhysicalDeviceFeatures.Query(mVkPhysicalDevice,mAPIVersion);
		InitDevice();
		SetupFormats();
		mFenceManager = new FenceManager();
		mFenceManager->Init(this);
		mImmediateContext=new CGIVulkanCommandListContextImmediate(&CGIVulkan::Get(),this,mGraphicsQueue);
		gCGICommandListExecutor.mCommandListImmediate.GraphicsContext=new CGIVulkanCommandListContext(&CGIVulkan::Get(), this, mGraphicsQueue, mImmediateContext);
	}
	void CGIVulkanDevice::InitDevice()
	{
		VkDeviceCreateInfo deviceCreateInfo = {};
		deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
		deviceCreateInfo.pEnabledFeatures = &mVulkanPhysicalDeviceFeatures.Core_1_0;
		const char* extensions[] = {
			VK_KHR_SWAPCHAIN_EXTENSION_NAME
		};
		deviceCreateInfo.ppEnabledExtensionNames = extensions;
		deviceCreateInfo.enabledExtensionCount=_countof(extensions);
		std::vector<VkDeviceQueueCreateInfo> QueueFamilyInfos;
		int32 GfxQueueFamilyIndex = -1;
		int32 ComputeQueueFamilyIndex = -1;
		int32 TransferQueueFamilyIndex = -1;
		uint32 NumPriorities = 0;
		for (uint32 FamilyIndex = 0; FamilyIndex < mVkQueueFamilyProperties.size(); ++FamilyIndex)
		{
			const VkQueueFamilyProperties& CurrProps = mVkQueueFamilyProperties[FamilyIndex];

			bool bIsValidQueue = false;
			if ((CurrProps.queueFlags & VK_QUEUE_GRAPHICS_BIT) == VK_QUEUE_GRAPHICS_BIT)
			{
				if (GfxQueueFamilyIndex == -1)
				{
					GfxQueueFamilyIndex = FamilyIndex;
					bIsValidQueue = true;
				}
				else
				{
					//#todo-rco: Support for multi-queue/choose the best queue!
				}
			}

			if ((CurrProps.queueFlags & VK_QUEUE_COMPUTE_BIT) == VK_QUEUE_COMPUTE_BIT)
			{
				if (ComputeQueueFamilyIndex == -1
					//&&(GRHIAllowAsyncComputeCvar.GetValueOnAnyThread() != 0 || GAllowPresentOnComputeQueue.GetValueOnAnyThread() != 0)
					&& GfxQueueFamilyIndex != FamilyIndex)
				{
					ComputeQueueFamilyIndex = FamilyIndex;
					bIsValidQueue = true;
				}
			}

			if ((CurrProps.queueFlags & VK_QUEUE_TRANSFER_BIT) == VK_QUEUE_TRANSFER_BIT)
			{
				// Prefer a non-gfx transfer queue
				if (TransferQueueFamilyIndex == -1 && (CurrProps.queueFlags & VK_QUEUE_GRAPHICS_BIT) != VK_QUEUE_GRAPHICS_BIT && (CurrProps.queueFlags & VK_QUEUE_COMPUTE_BIT) != VK_QUEUE_COMPUTE_BIT)
				{
					TransferQueueFamilyIndex = FamilyIndex;
					bIsValidQueue = true;
				}
			}

			if (!bIsValidQueue)
			{
				continue;
			}

			uint32 QueueIndex = QueueFamilyInfos.size();
			VkDeviceQueueCreateInfo CurrQueue = {};
			CurrQueue.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
			CurrQueue.queueFamilyIndex = FamilyIndex;
			CurrQueue.queueCount = CurrProps.queueCount;
			QueueFamilyInfos.push_back(CurrQueue);
			NumPriorities += CurrProps.queueCount;
		}
		std::vector<float> QueuePriorities;
		QueuePriorities.resize(NumPriorities);
		float* CurrentPriority = QueuePriorities.data();
		for (uint32 Index = 0; Index < QueueFamilyInfos.size(); ++Index)
		{
			VkDeviceQueueCreateInfo& CurrQueue = QueueFamilyInfos[Index];
			CurrQueue.pQueuePriorities = CurrentPriority;

			const VkQueueFamilyProperties& CurrProps = mVkQueueFamilyProperties[CurrQueue.queueFamilyIndex];
			for (int32 QueueIndex = 0; QueueIndex < (int32)CurrProps.queueCount; ++QueueIndex)
			{
				*CurrentPriority++ = 1.0f;
			}
		}
		deviceCreateInfo.queueCreateInfoCount = QueueFamilyInfos.size();
		deviceCreateInfo.pQueueCreateInfos = QueueFamilyInfos.data();
		vkCreateDevice(mVkPhysicalDevice,&deviceCreateInfo,nullptr,&mDevice);
		mGraphicsQueue = new CGIVulkanQueue(this, GfxQueueFamilyIndex);
		if (ComputeQueueFamilyIndex == -1)
		{
			// If we didn't find a dedicated Queue, use the default one
			ComputeQueueFamilyIndex = GfxQueueFamilyIndex;
		}
		mComputeQueue = new CGIVulkanQueue(this, ComputeQueueFamilyIndex);
		if (TransferQueueFamilyIndex == -1)
		{
			// If we didn't find a dedicated Queue, use the default one
			TransferQueueFamilyIndex = ComputeQueueFamilyIndex;
		}
		mTransferQueue = new CGIVulkanQueue(this, TransferQueueFamilyIndex);
	}
	void CGIVulkanDevice::SetupPresentQueue(VkSurfaceKHR inSurface)
	{
		if(mPresentQueue!=NULL)
		{
			return ;
		}
		const auto SupportsPresent = [inSurface](VkPhysicalDevice PhysicalDevice, CGIVulkanQueue* Queue)
		{
			VkBool32 bSupportsPresent = VK_FALSE;
			const uint32 FamilyIndex = Queue->GetFamilyIndex();
			vkGetPhysicalDeviceSurfaceSupportKHR(PhysicalDevice, FamilyIndex, inSurface, &bSupportsPresent);
			return (bSupportsPresent == VK_TRUE);
		};
		if(mComputeQueue->GetFamilyIndex() != mGraphicsQueue->GetFamilyIndex() && SupportsPresent(mVkPhysicalDevice,mComputeQueue))
		{
			mPresentQueue=mComputeQueue;
		}else{
			mPresentQueue=mGraphicsQueue;
		}
	}
	void CGIVulkanDevice::SetupFormats()
	{
		for (uint32 Index = 0; Index < VK_FORMAT_RANGE_SIZE; ++Index)
		{
			const VkFormat Format = (VkFormat)Index;
			mFormatProperties[Index]={};
			vkGetPhysicalDeviceFormatProperties(mVkPhysicalDevice, Format, &mFormatProperties[Index]);
		}
		// Create shortcuts for the possible component mappings
		const VkComponentMapping ComponentMappingRGBA = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
		const VkComponentMapping ComponentMappingRGB1 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE };
		const VkComponentMapping ComponentMappingRG01 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE };
		const VkComponentMapping ComponentMappingR001 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE };
		const VkComponentMapping ComponentMappingRIII = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
		const VkComponentMapping ComponentMapping000R = { VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R };
		const VkComponentMapping ComponentMappingR000 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO };
		const VkComponentMapping ComponentMappingRR01 = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE };


		// Initialize the platform pixel format map.
		for (int32 Index = 0; Index < PF_MAX; ++Index)
		{
			GPixelFormats[Index].PlatformFormat = VK_FORMAT_UNDEFINED;
			GPixelFormats[Index].Supported = false;
			GVulkanBufferFormat[Index] = VK_FORMAT_UNDEFINED;
			
			// Set default component mapping
			PixelFormatComponentMapping[Index] = ComponentMappingRGBA;
		}

		const EPixelFormatCapabilities ColorRenderTargetRequiredCapabilities = (EPixelFormatCapabilities::TextureSample | EPixelFormatCapabilities::RenderTarget);

		// Default formats
		MapFormatSupport(EPixelFormat::PF_B8G8R8A8, { VK_FORMAT_B8G8R8A8_UNORM }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_G8, { VK_FORMAT_R8_UNORM }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_FloatRGB, { VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_R16G16B16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGB1, ColorRenderTargetRequiredCapabilities);
		MapFormatSupport(EPixelFormat::PF_FloatRGBA, { VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGBA, 8);
		MapFormatSupport(EPixelFormat::PF_ShadowDepth, { VK_FORMAT_D16_UNORM }, ComponentMappingRIII);
		MapFormatSupport(EPixelFormat::PF_G32R32F, { VK_FORMAT_R32G32_SFLOAT }, ComponentMappingRG01, 8);  // Requirement for GPU particles
		MapFormatSupport(EPixelFormat::PF_A32B32G32R32F, { VK_FORMAT_R32G32B32A32_SFLOAT }, ComponentMappingRGBA, 16);
		MapFormatSupport(EPixelFormat::PF_G16R16, { VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16_SFLOAT }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_G16R16F, { VK_FORMAT_R16G16_SFLOAT }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_G16R16F_FILTER, { VK_FORMAT_R16G16_SFLOAT }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_R16_UINT, { VK_FORMAT_R16_UINT }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_R16_SINT, { VK_FORMAT_R16_SINT }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_R32_UINT, { VK_FORMAT_R32_UINT }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_R32_SINT, { VK_FORMAT_R32_SINT }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_R8_UINT, { VK_FORMAT_R8_UINT }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_D24, { VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT }, ComponentMappingR000);
		MapFormatSupport(EPixelFormat::PF_R16F, { VK_FORMAT_R16_SFLOAT }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_R16F_FILTER, { VK_FORMAT_R16_SFLOAT }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_FloatR11G11B10, { VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_R16G16B16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGB1, ColorRenderTargetRequiredCapabilities);
		MapFormatSupport(EPixelFormat::PF_A2B10G10R10, { VK_FORMAT_A2B10G10R10_UNORM_PACK32 }, ComponentMappingRGBA, 4);
		MapFormatSupport(EPixelFormat::PF_A16B16G16R16, { VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGBA, 8);
		MapFormatSupport(EPixelFormat::PF_A8, { VK_FORMAT_R8_UNORM }, ComponentMapping000R);
		MapFormatSupport(EPixelFormat::PF_R5G6B5_UNORM, { VK_FORMAT_R5G6B5_UNORM_PACK16 }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_B5G5R5A1_UNORM, { VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_R5G5B5A1_UNORM_PACK16, VK_FORMAT_B8G8R8A8_UNORM }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_R8G8B8A8, { VK_FORMAT_R8G8B8A8_UNORM }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_R8G8B8A8_UINT, { VK_FORMAT_R8G8B8A8_UINT }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_R8G8B8A8_SNORM, { VK_FORMAT_R8G8B8A8_SNORM }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_R16G16_UINT, { VK_FORMAT_R16G16_UINT }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_R16G16_SINT, { VK_FORMAT_R16G16_SINT }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_R16G16B16A16_UINT, { VK_FORMAT_R16G16B16A16_UINT }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_R16G16B16A16_SINT, { VK_FORMAT_R16G16B16A16_SINT }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_R32G32_UINT, { VK_FORMAT_R32G32_UINT }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_R32G32B32A32_UINT, { VK_FORMAT_R32G32B32A32_UINT }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_R16G16B16A16_SNORM, { VK_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_R16G16B16A16_UNORM, { VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_SFLOAT }, ComponentMappingRGBA);
		MapFormatSupport(EPixelFormat::PF_R8G8, { VK_FORMAT_R8G8_UNORM }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_V8U8, { VK_FORMAT_R8G8_UNORM }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_R32_FLOAT, { VK_FORMAT_R32_SFLOAT }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_R8, { VK_FORMAT_R8_UNORM }, ComponentMappingR001);
		MapFormatSupport(EPixelFormat::PF_G16R16_SNORM, { VK_FORMAT_R16G16_SNORM }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_R8G8_UINT, { VK_FORMAT_R8G8_UINT }, ComponentMappingRG01);
		MapFormatSupport(EPixelFormat::PF_R32G32B32_UINT, { VK_FORMAT_R32G32B32_UINT }, ComponentMappingRGB1);
		MapFormatSupport(EPixelFormat::PF_R32G32B32_SINT, { VK_FORMAT_R32G32B32_SINT }, ComponentMappingRGB1);
		MapFormatSupport(EPixelFormat::PF_R32G32B32F, { VK_FORMAT_R32G32B32_SFLOAT }, ComponentMappingRGB1);
		MapFormatSupport(EPixelFormat::PF_R8_SINT, { VK_FORMAT_R8_SINT }, ComponentMappingR001);

		// prefer VK_FORMAT_D24_UNORM_S8_UINT
		MapFormatSupport(EPixelFormat::PF_DepthStencil, { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT }, ComponentMappingRIII);
		MapFormatSupport(EPixelFormat::PF_X24_G8, { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT }, ComponentMappingRR01);
		GPixelFormats[(uint32)EPixelFormat::PF_DepthStencil].bIs24BitUnormDepthStencil = true;
		// Verify available Vertex Formats
		{
			static_assert(VET_None == 0, "Change loop below to skip VET_None");
			for (int32 VETIndex = (int32)VET_None + 1; VETIndex < VET_MAX; ++VETIndex)
			{
				const EVertexElementType UEType = (EVertexElementType)VETIndex;
				const VkFormat VulkanFormat = UEToVkBufferFormat(UEType);
				const VkFormatProperties& VertexFormatProperties = GetFormatProperties(VulkanFormat);
			}
		}

		// Verify the potential SRGB formats and fill GVulkanSRGBFormat
		{
			auto GetSRGBMapping = [this](const VkFormat InFormat)
			{
				VkFormat SRGBFormat = InFormat;
				switch (InFormat)
				{
				case VK_FORMAT_B8G8R8A8_UNORM:				SRGBFormat = VK_FORMAT_B8G8R8A8_SRGB; break;
				case VK_FORMAT_A8B8G8R8_UNORM_PACK32:		SRGBFormat = VK_FORMAT_A8B8G8R8_SRGB_PACK32; break;
				case VK_FORMAT_R8_UNORM:					SRGBFormat = VK_FORMAT_R8_UNORM; break;
				case VK_FORMAT_R8G8_UNORM:					SRGBFormat = VK_FORMAT_R8G8_SRGB; break;
				case VK_FORMAT_R8G8B8_UNORM:				SRGBFormat = VK_FORMAT_R8G8B8_SRGB; break;
				case VK_FORMAT_R8G8B8A8_UNORM:				SRGBFormat = VK_FORMAT_R8G8B8A8_SRGB; break;
				case VK_FORMAT_BC1_RGB_UNORM_BLOCK:			SRGBFormat = VK_FORMAT_BC1_RGB_SRGB_BLOCK; break;
				case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_BC1_RGBA_SRGB_BLOCK; break;
				case VK_FORMAT_BC2_UNORM_BLOCK:				SRGBFormat = VK_FORMAT_BC2_SRGB_BLOCK; break;
				case VK_FORMAT_BC3_UNORM_BLOCK:				SRGBFormat = VK_FORMAT_BC3_SRGB_BLOCK; break;
				case VK_FORMAT_BC7_UNORM_BLOCK:				SRGBFormat = VK_FORMAT_BC7_SRGB_BLOCK; break;
				case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; break;
				case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:	SRGBFormat = VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; break;
				case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:	SRGBFormat = VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_4x4_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_5x4_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_5x5_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_6x5_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_6x6_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_8x5_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_8x6_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_8x8_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_10x5_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_10x6_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_10x8_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_10x10_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_12x10_SRGB_BLOCK; break;
				case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:		SRGBFormat = VK_FORMAT_ASTC_12x12_SRGB_BLOCK; break;
					//		case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:	Format = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG; break;
					//		case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:	Format = VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG; break;
					//		case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:	Format = VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG; break;
					//		case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:	Format = VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG; break;
				default:	break;
				}

				return SRGBFormat;
			};

			for (int32 PixelFormatIndex = 0; PixelFormatIndex < PF_MAX; ++PixelFormatIndex)
			{
				const FPixelFormatInfo& PixelFormatInfo = GPixelFormats[PixelFormatIndex];
				if (PixelFormatInfo.Supported)
				{
					const VkFormat OriginalFormat = (VkFormat)PixelFormatInfo.PlatformFormat;
					GVulkanSRGBFormat[PixelFormatIndex] = GetSRGBMapping(OriginalFormat);
				}
				else
				{
					GVulkanSRGBFormat[PixelFormatIndex] = VK_FORMAT_UNDEFINED;
				}
			}
		}
	}
	void CGIVulkanDevice::MapImageFormatSupport(FPixelFormatInfo& PixelFormatInfo, const std::vector<VkFormat>& PrioritizedFormats, EPixelFormatCapabilities RequiredCapabilities)
	{
		// Query for MipMap support with typical parameters
		auto SupportsMipMap = [this](VkFormat InFormat)
		{
			VkImageFormatProperties ImageFormatProperties;
			VkResult RetVal = vkGetPhysicalDeviceImageFormatProperties(mVkPhysicalDevice, InFormat, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT, 0, &ImageFormatProperties);
			return (RetVal == VK_SUCCESS) && (ImageFormatProperties.maxMipLevels > 1);
		};

		EPixelFormatCapabilities Capabilities = EPixelFormatCapabilities::None;
		auto ConvertImageCap = [&Capabilities](const VkFormatProperties& InFormatProperties, EPixelFormatCapabilities UnrealCap, VkFormatFeatureFlags InFlag)
		{
			// Do not distinguish between Linear and Optimal for now.
			const bool HasImageFeature = VKHasAllFlags(InFormatProperties.linearTilingFeatures, InFlag) || VKHasAllFlags(InFormatProperties.optimalTilingFeatures, InFlag);
			if (HasImageFeature)
			{
				EnumAddFlags(Capabilities, UnrealCap);
			}
		};

		// Go through the PrioritizedFormats and use the first one that meets RequiredCapabilities
		for (uint32 FormatIndex = 0; FormatIndex < PrioritizedFormats.size(); ++FormatIndex)
		{
			Capabilities = EPixelFormatCapabilities::None;

			const VkFormat VulkanFormat = PrioritizedFormats[FormatIndex];
			const VkFormatProperties& LocalFormatProperties = GetFormatProperties(VulkanFormat);

			// Check for individual texture caps
			ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::AnyTexture | EPixelFormatCapabilities::TextureSample | EPixelFormatCapabilities::TextureLoad, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
			ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::DepthStencil, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
			ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::RenderTarget, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
			ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::TextureBlendable, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT);
			ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::AllUAVFlags | EPixelFormatCapabilities::TextureStore, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);

			ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::TextureAtomics, VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT);
			ConvertImageCap(LocalFormatProperties, EPixelFormatCapabilities::TextureFilterable, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT);

			if (EnumHasAllFlags(Capabilities, EPixelFormatCapabilities::AnyTexture))
			{
				// We support gather, but some of our shaders assume offsets so check against features
				if (mVulkanPhysicalDeviceFeatures.Core_1_0.shaderImageGatherExtended)
				{
					EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureGather);
				}

				if (SupportsMipMap(VulkanFormat))
				{
					EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureMipmaps);
				}
			}

			if (EnumHasAllFlags(Capabilities, RequiredCapabilities))
			{
				PixelFormatInfo.PlatformFormat = VulkanFormat;
				PixelFormatInfo.Capabilities |= Capabilities;

				break;
			}
		}
	}
	void CGIVulkanDevice::MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> InPrioritizedFormats, const VkComponentMapping& ComponentMapping, EPixelFormatCapabilities RequiredCapabilities, int32 BlockBytes)
	{
		std::vector<VkFormat> PrioritizedFormats(InPrioritizedFormats);
		FPixelFormatInfo& PixelFormatInfo = GPixelFormats[(uint32)UEFormat];
		MapBufferFormatSupport(PixelFormatInfo, UEFormat, PrioritizedFormats[0]);
		MapImageFormatSupport(PixelFormatInfo, PrioritizedFormats, RequiredCapabilities);

		// Flag the pixel format as supported if we can do anything with it
		PixelFormatInfo.Supported = EnumHasAllFlags(PixelFormatInfo.Capabilities, RequiredCapabilities) || EnumHasAnyFlags(PixelFormatInfo.Capabilities, EPixelFormatCapabilities::Buffer);
		if (PixelFormatInfo.Supported)
		{
			PixelFormatComponentMapping[(uint32)UEFormat] = ComponentMapping;
			if (BlockBytes > 0)
			{
				PixelFormatInfo.BlockBytes = BlockBytes;
			}
		}
	}
	void CGIVulkanDevice::MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping)
	{
		MapFormatSupport(UEFormat, PrioritizedFormats, ComponentMapping, kDefaultTextureCapabilities, kDefaultBlockBytes);
	}
	void CGIVulkanDevice::MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping, int32 BlockBytes)
	{
		MapFormatSupport(UEFormat, PrioritizedFormats, ComponentMapping, kDefaultTextureCapabilities, BlockBytes);
	}
	void CGIVulkanDevice::MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping, EPixelFormatCapabilities RequiredCapabilities)
	{
		MapFormatSupport(UEFormat, PrioritizedFormats, ComponentMapping, RequiredCapabilities, kDefaultBlockBytes);
	}
	const VkFormatProperties& CGIVulkanDevice::GetFormatProperties(VkFormat InFormat) const
	{
		if (InFormat >= 0 && InFormat < VK_FORMAT_RANGE_SIZE)
		{
			return mFormatProperties[InFormat];
		}

		// Check for extension formats
		auto iter = mExtensionFormatProperties.find(InFormat);
		if (iter!=mExtensionFormatProperties.end())
		{
			return iter->second;
		}

		// Add it for faster caching next time
		VkFormatProperties NewProperties = {}; 
		vkGetPhysicalDeviceFormatProperties(mVkPhysicalDevice, InFormat, &NewProperties);
		mExtensionFormatProperties.insert(std::pair<VkFormat,VkFormatProperties>(InFormat,NewProperties));
		return NewProperties;
	}

	void CGIVulkanDevice::MapBufferFormatSupport(FPixelFormatInfo& PixelFormatInfo, EPixelFormat UEFormat, VkFormat VulkanFormat)
	{
		const VkFormatProperties& LocalFormatProperties = GetFormatProperties(VulkanFormat);
		EPixelFormatCapabilities Capabilities = EPixelFormatCapabilities::None;

		auto ConvertBufferCap = [&Capabilities, &LocalFormatProperties](EPixelFormatCapabilities UnrealCap, VkFormatFeatureFlags InFlag)
		{
			const bool HasBufferFeature = VKHasAllFlags(LocalFormatProperties.bufferFeatures, InFlag);
			if (HasBufferFeature)
			{
				EnumAddFlags(Capabilities, UnrealCap);
			}
		};

		// Check for buffer caps, use the first one with any caps
		if (LocalFormatProperties.bufferFeatures != 0)
		{
			EnumAddFlags(Capabilities, EPixelFormatCapabilities::Buffer);

			ConvertBufferCap(EPixelFormatCapabilities::VertexBuffer, VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT);
			ConvertBufferCap(EPixelFormatCapabilities::BufferLoad, VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT);
			ConvertBufferCap(EPixelFormatCapabilities::BufferStore, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT);
			ConvertBufferCap(EPixelFormatCapabilities::BufferAtomics, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT);

			// Vulkan index buffers aren't tied to formats, so any 16 or 32 bit UINT format with a single component will do...
			// But because we can't check for uint vs float, hardcode supported formats for now
			if (EnumHasAllFlags(Capabilities, (EPixelFormatCapabilities::BufferLoad | EPixelFormatCapabilities::BufferStore)) &&
				((VulkanFormat == VK_FORMAT_R16_UINT) || (VulkanFormat == VK_FORMAT_R32_UINT)))
			{
				EnumAddFlags(Capabilities, EPixelFormatCapabilities::IndexBuffer);
			}

			GVulkanBufferFormat[(uint32)UEFormat] = VulkanFormat;
			PixelFormatInfo.Capabilities |= Capabilities;
		}
	}
}